package com.zh.authserver.config;

import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.jackson2.SecurityJackson2Modules;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;

import java.time.Duration;
import java.time.Instant;
import java.time.temporal.TemporalUnit;
import java.util.List;

/**
 * 客户端配置
 * 它的作用是注册和管理所有允许访问授权服务器的客户端应用，是 OAuth2 授权流程的关键配置。
 */
@Configuration
public class ClientConfig {
    private static final Duration CLIENT_SECRET_3month = Duration.ofDays(90);  // 密钥有效期：3个月

    // 使用数据库存储客户端配置（替代内存存储）
    /*@Bean
    public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {
        // 初始化数据库表结构（仅首次启动时需要）
        // 表结构定义：https://github.com/spring-projects/spring-authorization-server/blob/main/oauth2-authorization-server/src/main/resources/org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql
        return new JdbcRegisteredClientRepository(jdbcTemplate);
    }*/

    // 配置客户端注册仓库
    @Bean
    public RegisteredClientRepository registeredClientRepository(PasswordEncoder passwordEncoder) {
        // 创建一个示例客户端
        RegisteredClient freechat = RegisteredClient.withId("freechat")
                .clientId("freechat2024")
                .clientSecret(passwordEncoder.encode("secret"))
//                .clientSecretExpiresAt(Instant.now().plus(CLIENT_SECRET_3month)) //3个月
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
                .authorizationGrantTypes(grantTypes -> {
                    grantTypes.add(AuthorizationGrantType.AUTHORIZATION_CODE); //授权码模式，用于用户授权
                    grantTypes.add(AuthorizationGrantType.CLIENT_CREDENTIALS); //客户端模式，用于服务端通信
                    grantTypes.add(AuthorizationGrantType.REFRESH_TOKEN); //刷新令牌模式，用于获取新的访问令牌
                })
                .tokenSettings(
                        TokenSettings.builder()
                                .accessTokenTimeToLive(Duration.ofDays(1))
                                .refreshTokenTimeToLive(Duration.ofDays(7))
                                .reuseRefreshTokens(true).build()
                )
                .clientSettings(ClientSettings.builder().requireAuthorizationConsent(false).build())
                .scopes(scopes -> {
                    scopes.add("read");
                    scopes.add("write");
                    scopes.add("share");
                    scopes.add(OidcScopes.OPENID);
                    scopes.add(OidcScopes.PROFILE);
                })
                .redirectUri("https://www.baidu.com")
                .redirectUri("http://localhost:8089/login/oauth2/code/freechat")
                .build();

        // 创建一个示例客户端
        RegisteredClient monoblog = RegisteredClient.withId("monoblog")
                .clientId("monoblog2025")
                .clientSecret(passwordEncoder.encode("secret"))
//                .clientSecretExpiresAt(Instant.now().plus(CLIENT_SECRET_3month)) //3个月
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
                .authorizationGrantTypes(grantTypes -> {
                    grantTypes.add(AuthorizationGrantType.AUTHORIZATION_CODE); //授权码模式，用于用户授权
                    grantTypes.add(AuthorizationGrantType.CLIENT_CREDENTIALS); //客户端模式，用于服务端通信
                    grantTypes.add(AuthorizationGrantType.REFRESH_TOKEN); //刷新令牌模式，用于获取新的访问令牌
                })
                .tokenSettings(
                        TokenSettings.builder()
                                .accessTokenTimeToLive(Duration.ofDays(1))
                                .refreshTokenTimeToLive(Duration.ofDays(7))
                                .reuseRefreshTokens(true).build()
                )
                .clientSettings(ClientSettings.builder().requireAuthorizationConsent(false).build())
                .scopes(scopes -> {
                    scopes.add("read");
                    scopes.add("write");
                    scopes.add("share");
                    scopes.add(OidcScopes.OPENID);
                    scopes.add(OidcScopes.PROFILE);
                })
                .redirectUri("https://www.baidu.com")
                .redirectUri("http://localhost:8089/login/oauth2/code/monoblog")
                .build();

        return new InMemoryRegisteredClientRepository(freechat,monoblog);
    }


    /**
     * 保存授权信息，授权服务器给我们颁发来token，那我们肯定需要保存吧，由这个服务来保存
     */
    @Bean
    public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
        JdbcOAuth2AuthorizationService service = new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);
        JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper rowMapper = new JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper(registeredClientRepository);

        ClassLoader classLoader = JdbcOAuth2AuthorizationService.class.getClassLoader();
        List<Module> modules = SecurityJackson2Modules.getModules(classLoader);

        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModules(modules);
        objectMapper.registerModule(new OAuth2AuthorizationServerJackson2Module());

        rowMapper.setObjectMapper(objectMapper);
        service.setAuthorizationRowMapper(rowMapper);
        return service;
    }
}

